﻿/*
 * Copyright (c) 2015-2016 Constantijn Evenhuis (blueprints@const.nl)
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
using Blueprints.Editing.Actions;
using Blueprints.Validation;
using System;
using System.Collections;
using System.Collections.Generic;

namespace Blueprints.Editing
{
    partial class EditNode
    {
        /// <summary>
        /// A collection of EditNode elements.
        /// </summary>
        public sealed class ElementCollection : IReadOnlyList<EditNode>
        {
            internal ElementCollection(EditNode declaringNode)
            {
                DeclaringNode = declaringNode;
            }

            /// <summary>
            /// Gets the number of nodes in this collection.
            /// </summary>
            public int Count
            {
                get
                {
                    IValidatedNode validation;
                    if (DeclaringNode.TryGetValidatedNode(out validation))
                    {
                        return validation.Elements.Count;
                    }

                    return 0;
                }
            }

            internal EditNode DeclaringNode
            {
                get;
                private set;
            }

            /// <summary>
            /// Gets the node at <paramref name="index"/>.
            /// </summary>
            /// <param name="index">The index of the node.</param>
            /// <returns>The node at <paramref name="index"/>.</returns>
            public EditNode this[int index]
            {
                get
                {
                    IValidatedNode validation;
                    if (DeclaringNode.TryGetValidatedNode(out validation))
                    {
                        IValidatedNode validatedElement = validation.Elements[index];
                        return DeclaringNode.EditContext.GetEditNode(validatedElement);
                    }

                    throw new IndexOutOfRangeException();
                }
            }

            /// <summary>
            /// Gets an enumerator that iterates through the nodes.
            /// </summary>
            /// <returns>An enumerator that iterates through the nodes.</returns>
            public IEnumerator<EditNode> GetEnumerator()
            {
                IValidatedNode validation;
                if (!DeclaringNode.TryGetValidatedNode(out validation))
                {
                    return new List<EditNode>(0).GetEnumerator();
                }

                NodeCollection nodes = DeclaringNode.EditContext.Nodes;
                List<EditNode> elements = new List<EditNode>(validation.Elements.Count);
                foreach (IValidatedNode element in validation.Elements)
                {
                    EditNode editNode = DeclaringNode.EditContext.GetEditNode(element);
                    if (editNode == null)
                    {
                        continue;
                    }

                    elements.Add(editNode);
                }

                return elements.GetEnumerator();
            }

            IEnumerator IEnumerable.GetEnumerator()
            {
                return GetEnumerator();
            }

            /// <summary>
            /// Inserts a new node at <paramref name="index"/>.
            /// </summary>
            /// <param name="index">The index at which to insert the node.</param>
            /// <returns>The inserted node.</returns>
            public EditNode Insert(int index)
            {
                return DeclaringNode.EditContext.Do(new InsertElementAction(DeclaringNode, index));
            }

            /// <summary>
            /// Adds a node to the end of this collection.
            /// </summary>
            /// <returns>The added node.</returns>
            public EditNode Add()
            {
                return DeclaringNode.EditContext.Do(new AddElementAction(DeclaringNode));
            }

            /// <summary>
            /// Removes the node at <paramref name="index"/>.
            /// </summary>
            /// <param name="index">The index of the node to remove.</param>
            public void RemoveAt(int index)
            {
                DeclaringNode.EditContext.Do(new RemoveNodeAction(this[index], DeclaringNode));
            }

            /// <summary>
            /// Removes <paramref name="node"/> from the collection.
            /// </summary>
            /// <param name="node">The node to remove.</param>
            /// <returns>True if the node was removed, false if the node was not found in the collection.</returns>
            public bool Remove(EditNode node)
            {
                for (int i = 0; i < Count; i++)
                {
                    if (this[i] == node)
                    {
                        RemoveAt(i);
                        return true;
                    }
                }

                return false;
            }
        }
    }
}